package com.rpoonolly.simplegame;

import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

import com.rpoonolly.simplegame.animation.PlayerAnimator;
import com.rpoonolly.simplegame.animation.SimpleNPCAnimator;
import com.rpoonolly.simplegame.animation.TexturedDynamicEntity;
import com.rpoonolly.simplegame.core.GameMap;
import com.rpoonolly.simplegame.core.MapFileManager;
import com.rpoonolly.simplegame.core.aabb.AABBCollisionManager;
import com.rpoonolly.simplegame.core.aabb.AABBEntity;
import com.rpoonolly.simplegame.editor.SimpleEditorProcessor;
import com.rpoonolly.simplegame.editor.SwingEntityEditRenderer;
import com.rpoonolly.simplegame.editor.WorldGridEditRenderer;
import com.rpoonolly.simplegame.mapio.SuperSimpleMapFileMngr;
import com.rpoonolly.simplegame.processors.GhostProcessor;
import com.rpoonolly.simplegame.processors.SimpleNPCProcessor;
import com.rpoonolly.simplegame.processors.SimplePlayerProcessor;
import com.rpoonolly.simplegame.renderer.SwingEntityRenderer;
import com.rpoonolly.simplegame.textures.TextureLoader;
import com.rpoonolly.simplegame.worldGrid.WorldGrid;
import com.rpoonolly.simplegame.worldGrid.WorldGridGameMap;
import com.rpoonolly.simplegame.worldGrid.WorldGridRenderer;

@SuppressWarnings("serial")
public class SimpleGame extends JPanel implements ActionListener {
	private JFrame window;
	private int windowWidth = Config.WINDOW_DEFAULT_WIDTH;
	private int windowHeight = Config.WINDOW_DEFAULT_HEIGHT;
	
	private Timer timer;
	private SimpleCountingInputManager<SimpleUserAction> inputMngr;
	private SwingEngine engine;
	private MapFileManager<GameMap> mapFileMngr;
	private WorldGridGameMap gameMap;
	private TextureLoader textureLoader = new TextureLoader();
	
	private AABBEntity player;
	
	private SwingEntityRenderer entityRenderer;
	private WorldGridRenderer worldGridRenderer;
	
	private SimpleEditorProcessor processor_editor;
	private SimplePlayerProcessor processor_player;
	private SimpleNPCProcessor processor_enemy;
	private GhostProcessor processor_ghost;
	private PlayerAnimator animator_player;
	private SimpleNPCAnimator animator_bee;
	private SimpleNPCAnimator animator_ghost;
	
	public SimpleGame() throws IOException {
		Map<String, List<AABBEntity>> entities;
		AABBCollisionManager collisionMngr = new AABBCollisionManager();
		
		window = new JFrame(Config.GAME_TITLE);
		window.setSize(windowWidth, windowHeight);
		window.setContentPane(this);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		Map<Integer, SimpleUserAction> keyActionMap = new HashMap<Integer, SimpleUserAction>(4);
		keyActionMap.put(KeyEvent.VK_UP, SimpleUserAction.PLAYER_MOVE_UP);
		keyActionMap.put(KeyEvent.VK_DOWN, SimpleUserAction.PLAYER_MOVE_DOWN);
		keyActionMap.put(KeyEvent.VK_LEFT, SimpleUserAction.PLAYER_MOVE_LEFT);
		keyActionMap.put(KeyEvent.VK_RIGHT, SimpleUserAction.PLAYER_MOVE_RIGHT);
		keyActionMap.put(KeyEvent.VK_HOME, SimpleUserAction.EDITOR_TOGGLE_EDIT_MODE);
		keyActionMap.put(KeyEvent.VK_E, SimpleUserAction.EDITOR_CREATE_ENEMY);
		keyActionMap.put(KeyEvent.VK_W, SimpleUserAction.EDITOR_CREATE_WORLD_OBJ);
		keyActionMap.put(KeyEvent.VK_DELETE, SimpleUserAction.EDITOR_DELETE);
		keyActionMap.put(KeyEvent.VK_S, SimpleUserAction.EDITOR_SAVE);
		keyActionMap.put(KeyEvent.VK_L, SimpleUserAction.EDITOR_LOAD);
		keyActionMap.put(KeyEvent.VK_D, SimpleUserAction.EDITOR_TILE_PAINT);
		keyActionMap.put(KeyEvent.VK_F, SimpleUserAction.EDITOR_TILE_CLEAR);
		
		inputMngr = new SimpleCountingInputManager<SimpleUserAction>(keyActionMap);
		window.addKeyListener(inputMngr);
		
		timer = new Timer(Config.WINDOW_REDRAW_DELAY, this);
		timer.setDelay(Config.WINDOW_REDRAW_DELAY);
		timer.setRepeats(true);
		
		mapFileMngr = new SuperSimpleMapFileMngr();
		gameMap = (WorldGridGameMap) loadWorld(null);
		entities = gameMap.getEntities();
		player = entities.get(Config.ENT_GRP_PLAYER).get(0);
		
		entityRenderer = new SwingEntityEditRenderer(gameMap, textureLoader);
		entityRenderer.setWindowWidth(windowWidth);
		entityRenderer.setWindowHeight(windowHeight);
		
		worldGridRenderer = new WorldGridEditRenderer(gameMap, player, textureLoader);
		worldGridRenderer.setWindowWidth(windowWidth);
		worldGridRenderer.setWindowHeight(windowHeight);
		
		processor_editor = new SimpleEditorProcessor(collisionMngr, collisionMngr, gameMap, inputMngr, mapFileMngr, textureLoader);
		
		processor_player = new SimplePlayerProcessor(collisionMngr, collisionMngr, gameMap, inputMngr);
		processor_enemy = new SimpleNPCProcessor(collisionMngr, collisionMngr, gameMap, Config.ENT_GRP_ENEMIES);
		processor_ghost = new GhostProcessor(collisionMngr, collisionMngr, gameMap);
		
		animator_player = new PlayerAnimator(gameMap);
		animator_bee = new SimpleNPCAnimator(gameMap, Config.ENEMY_TEXTURE_MAP, Collections.singletonList(Config.ENT_GRP_ENEMIES));
		animator_ghost = new SimpleNPCAnimator(gameMap, Config.GHOST_TEXTURE_MAP, Collections.singletonList(Config.ENT_GRP_GHOSTS));
		
		engine = new SwingEngine();
		engine.addProcessor(processor_editor);
		engine.addProcessor(processor_player);
		engine.addProcessor(processor_enemy);
		engine.addProcessor(processor_ghost);
		engine.addProcessor(animator_player);
		engine.addProcessor(animator_bee);
		engine.addProcessor(animator_ghost);
		engine.addRenderer(worldGridRenderer);
		engine.addRenderer(entityRenderer);
		
		window.setVisible(true);
		timer.start();
	}
	
	private GameMap loadWorld(String filename) {
		GameMap loadedMap;
		Map<String, List<AABBEntity>> entities;
		
		if (filename == null)
			filename = Config.DEFAULT_MAP_NAME;
		
		try {
			loadedMap = mapFileMngr.loadMap(filename);
		} catch (IOException e) {
			e.printStackTrace();
			loadedMap = null;
		}
		
		if (loadedMap == null) {
			entities = new ConcurrentHashMap<String, List<AABBEntity>>();
			List<AABBEntity> worldObjs = new CopyOnWriteArrayList<AABBEntity>();//new ArrayList<AABBEntity>();
			List<AABBEntity> enemies = new CopyOnWriteArrayList<AABBEntity>();//new ArrayList<AABBEntity>();
			List<AABBEntity> ghosts = new CopyOnWriteArrayList<AABBEntity>();//new ArrayList<AABBEntity>();
			
			enemies.add(new TexturedDynamicEntity(100, 100, Config.ENEMY_WIDTH, Config.ENEMY_HEIGHT));
			ghosts.add(new TexturedDynamicEntity(150, 100, Config.GHOST_WIDTH, Config.GHOST_HEIGHT));
			worldObjs.add( new AABBEntity( 130, -123, 182, 8) );
			
			entities.put(Config.ENT_GRP_WORLD_OBJS, worldObjs);
			entities.put(Config.ENT_GRP_GHOSTS, ghosts);
			entities.put(Config.ENT_GRP_ENEMIES, enemies);
			entities.put(Config.ENT_GRP_PLAYER, Collections.<AABBEntity>singletonList(
					new TexturedDynamicEntity(0, 0, Config.PLAYER_WIDTH, Config.PLAYER_HEIGHT)));
			
			loadedMap = new WorldGridGameMap(entities, new WorldGrid(0, 0, 32, 32));
		}
		
		return loadedMap;
	}
	
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		engine.render(g);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		engine.process();
		repaint();
	}
	
	public static void main(String args[]) throws IOException {
		new SimpleGame();
	}
}
